using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Text;
using NCommet.Core;
using System.Workflow.Activities.Rules;
using System.Workflow.ComponentModel.Serialization;
using System.Xml;
using NCommet.Core.Agents;
using System.Collections.ObjectModel;
using System.Collections;
using System.Configuration;
using System.Diagnostics;
using log4net;

namespace NCommet.Modules.BRE
{
    public class TypeTreeValidatorImpl : ITypedTreeValidator
    {

        #region Constants

        private const string _CONFIG_SECTION_KEY = "NCommet.Modules.BRE";
        private const string _CONFIG_FILENAME_KEY = "RuleFile";
        private const string _CONFIG_PATHTYPE_KEY = "RuleFilePathType";

        private const string _CAN_SETROOT_KEY = "CanSetRoot";
        private const string _CAN_ATTACH_KEY = "CanAttach";
        private const string _CAN_REMOVE_KEY = "CanRemove";
        private const string _CAN_CONTAIN_KEY = "CanContain";

        #endregion

        #region Helper methods, properties and caching

        private ILog MyLog { get { return LogManager.GetLogger(typeof(TypeTreeValidatorImpl)); } }

        private NameValueCollection ConfigSection
        {
            get
            {
                return (NameValueCollection)ConfigurationManager.GetSection(_CONFIG_SECTION_KEY);
            }
        }

        private RuleDefinitions DeserializeRuleSetXml(string FileName)
        {
            WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
            RuleDefinitions ruleDefinitions = null;

            string FullFileName = FileName;
            if (this.ConfigSection[_CONFIG_PATHTYPE_KEY] == "Web")
                FullFileName = System.Web.HttpContext.Current.Server.MapPath(FileName);

            using (XmlTextReader reader = new XmlTextReader(FullFileName))
            {
                ruleDefinitions = (RuleDefinitions)serializer.Deserialize(reader);
                reader.Close();
            }

            return ruleDefinitions;
        }

        private RuleDefinitions ruleDefinitions = null;
        private RuleDefinitions RuleDefinitions
        {
            get
            {
                if (ruleDefinitions != null)
                    return ruleDefinitions;
                ruleDefinitions = DeserializeRuleSetXml(this.ConfigSection[_CONFIG_FILENAME_KEY]);
                return ruleDefinitions;
            }
        }

        #endregion

        #region ITypedTreeValidator Members

        public bool CanSetRoot(Item CandidateRoot)
        {
            ICollection<string> ValidationSummary;
            return CanSetRoot(CandidateRoot, out ValidationSummary);
        }

        public bool CanSetRoot(Item CandidateRoot, out ICollection<string> ValidationSummary)
        {
            ///////////////
            MyLog.Debug("------------------------------------------------------");
            LogStack();
            MyLog.DebugFormat("CandidateRoot : ({0}) {1}::{2} ", CandidateRoot.Id, CandidateRoot.Name, CandidateRoot.ItemType);
            //////////////

            RECanSetRootInput input = new RECanSetRootInput(CandidateRoot, MyLog);
            RuleSet ruleSet = RuleDefinitions.RuleSets[_CAN_SETROOT_KEY];
            RuleEngine engine = new RuleEngine(ruleSet, typeof(RECanSetRootInput));
            engine.Execute(input);

            ValidationSummary = input.errors;
            return input.isValid;
        }

        public bool CanAttach(Item Parent, Item CandidateChild)
        {
            ICollection<string> ErrorMessages;
            return CanAttach(Parent, CandidateChild, out ErrorMessages);
        }

        private void LogStack()
        {
            StackFrame fr = new StackFrame(1, true);
            StackTrace st = new StackTrace(fr);
            MyLog.DebugFormat("{0} {1}", fr.GetMethod().Name, st.ToString().TrimEnd());
        }

        public bool CanAttach(Item Parent, Item CandidateChild, out ICollection<string> ValidationSummary)
        {
            if (Parent == null)
                throw new ArgumentNullException("Parent");
            if (CandidateChild == null)
                throw new ArgumentNullException("CandidateChild");

            ///////////////
            MyLog.Debug("------------------------------------------------------");
            LogStack();
            MyLog.DebugFormat("Parent : ({0}) {4}::{1} , CandidateChild : ({2}) {5}::{3}", (Parent != null ? Parent.Id : 0), (Parent != null ? (Parent.ItemType != null ? Parent.ItemType : "null type") : "null"), CandidateChild.Id, CandidateChild.ItemType, (Parent != null ? Parent.Name : "null name"), CandidateChild.Name);
            //////////////

            RECanAttachInput input = new RECanAttachInput(Parent, CandidateChild, MyLog);
            RuleSet ruleSet = RuleDefinitions.RuleSets[_CAN_ATTACH_KEY];
            RuleEngine engine = new RuleEngine(ruleSet, typeof(RECanAttachInput));
            engine.Execute(input);

            ValidationSummary = input.errors;
            return input.isValid;
        }

        public bool CanRemove(Item Parent, Item Child)
        {
            ICollection<string> ValidationSummary;
            return CanRemove(Parent, Child, out ValidationSummary);
        }

        public bool CanRemove(Item Parent, Item Child, out ICollection<string> ValidationSummary)
        {
            if (Parent == null)
                throw new ArgumentNullException("Parent");
            if (Child == null)
                throw new ArgumentNullException("Child");

            ///////////////
            MyLog.Debug("------------------------------------------------------");
            LogStack();
            MyLog.DebugFormat("Parent : ({0}) {4}::{1} , Child : ({2}) {5}::{3}", (Parent != null ? Parent.Id : 0), (Parent != null ? (Parent.ItemType != null ? Parent.ItemType : "null type") : "null"), Child.Id, Child.ItemType, (Parent != null ? Parent.Name : "null name"), Child.Name);
            //////////////

            RECanRemoveInput input = new RECanRemoveInput(Parent, Child, MyLog);
            RuleSet ruleSet = RuleDefinitions.RuleSets[_CAN_REMOVE_KEY];
            RuleEngine engine = new RuleEngine(ruleSet, typeof(RECanRemoveInput));
            engine.Execute(input);

            ValidationSummary = input.errors;
            return input.isValid;
        }

        public bool CanContain(Item Container, ContentBase CandidateContent)
        {
            ICollection<string> ErrorMessages;
            return CanContain(Container, CandidateContent, out ErrorMessages);
        }

        public bool CanContain(Item Container, ContentBase CandidateContent, out ICollection<string> ValidationSummary)
        {
            if (Container == null)
                throw new ArgumentNullException("Container");
            if (CandidateContent == null)
                throw new ArgumentNullException("CandidateContent");

            RECanContainInput input = new RECanContainInput(Container, CandidateContent, MyLog);
            RuleSet ruleSet = RuleDefinitions.RuleSets[_CAN_CONTAIN_KEY];
            RuleEngine engine = new RuleEngine(ruleSet, typeof(RECanContainInput));
            engine.Execute(input);

            ValidationSummary = input.errors;
            return input.isValid;
        }

        public ICollection<string> GetValidChildItemTypes(Item Parent)
        {
            RECanAttachInput input = new RECanAttachInput(Parent, null, MyLog);
            input.doGet = true;
            RuleSet ruleSet = RuleDefinitions.RuleSets[_CAN_ATTACH_KEY];
            RuleEngine engine = new RuleEngine(ruleSet, typeof(RECanAttachInput));
            engine.Execute(input);
            return ((input.tbl[Parent.ItemType] != null) ? new List<string>(input.tbl[Parent.ItemType].Split(new string[] { "," }, System.StringSplitOptions.RemoveEmptyEntries)) : new List<string>());
        }

        public ICollection<string> GetValidRootItemTypes()
        {
            RECanSetRootInput input = new RECanSetRootInput(null, MyLog);
            input.doGet = true;
            RuleSet ruleSet = RuleDefinitions.RuleSets[_CAN_SETROOT_KEY];
            RuleEngine engine = new RuleEngine(ruleSet, typeof(RECanSetRootInput));
            engine.Execute(input);
            return (input.tbl);
        }

        public ICollection<string> GetValidContentTypes(Item Container)
        {
            RECanContainInput input = new RECanContainInput(Container, null, MyLog);
            input.doGet = true;
            RuleSet ruleSet = RuleDefinitions.RuleSets[_CAN_CONTAIN_KEY];
            RuleEngine engine = new RuleEngine(ruleSet, typeof(RECanContainInput));
            engine.Execute(input);
            return ((input.tbl[Container.ItemType] != null) ? new List<string>(input.tbl[Container.ItemType].Split(new string[] { "," }, System.StringSplitOptions.RemoveEmptyEntries)) : new List<string>());
        }

        #endregion

    }
}
